package com.polytech.diploma.OracleStat;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class SQLStatData {

	protected String sqlText;
	protected int count = 1;
	protected int updateNum = 0;
	
	protected CommandType commandType;	/*Oracle command type definition*/
	
	/**
	 * Memory statistics
	 */
	protected int runtimeMemory;	/*Fixed amount of memory required during the execution of the child cursor*/
	protected int persistentMemory;	/*Fixed amount of memory used for the lifetime of the child cursor (in bytes)*/
	/**
	 * IO statistics
	 */
	protected int diskReads;		/*Number of disk reads for this child cursor*/
	protected int bufferGets;		/*Number of buffer gets for this child cursor*/
	protected int userIOWaitTime;	/*User I/O Wait Time (in microseconds)*/	
	/**
	 * General cost
	 */
	protected int rowsProcessed;	/*Total number of rows the parsed SQL statement returns*/
	protected int optimizerCost;	/*Cost of this query given by the optimizer*/
	/**
	 * Query duration
	 */
	protected int elapsedTime;		/*Elapsed time (in microseconds) used by this cursor for parsing, executing, and fetching*/
	
	/**
	 * Modification Frequency
	 */
	protected double updateFrequency;
	protected Date firstLoadTime = null;
	
	private Set<String> tableNames = null;
	private Map<String, List<String>> replaceList = null;
	
	private SQLStatData(SQLStatDataBuilder builder) {
		this.sqlText = builder.sqlText;
		this.count = builder.count;
		this.runtimeMemory = builder.runtimeMemory;
		this.persistentMemory = builder.persistentMemory;
		this.diskReads = builder.diskReads;
		this.bufferGets = builder.bufferGets;
		this.userIOWaitTime = builder.userIOWaitTime;
		this.rowsProcessed = builder.rowsProcessed;
		this.elapsedTime = builder.elapsedTime;
		this.commandType = builder.commandType;
		this.firstLoadTime = builder.firstLoadTime;
		this.optimizerCost = builder.optimizerCost;
		this.replaceList = builder.replaceList;
		
		this.tableNames = new HashSet<String>();
		updateFrequency = 0;
	}
	
	public static class SQLStatDataBuilder {
		
		protected String sqlText;
		protected int count = 1;
		protected int runtimeMemory;
		protected int persistentMemory;
		protected int diskReads;
		protected int bufferGets;
		protected int userIOWaitTime;
		protected int rowsProcessed;
		protected int optimizerCost;
		protected int elapsedTime;
		protected CommandType commandType;
		protected Date firstLoadTime = null;
		private Map<String, List<String>> replaceList = new HashMap<String, List<String>>();
		
		public SQLStatData build()
		{
			return new SQLStatData(this);
		}
		
		public SQLStatDataBuilder setSqlText(String sqlText) {
			this.sqlText = sqlText;
			return this;
		}
		public SQLStatDataBuilder setCount(int count) {
			this.count = count;
			return this;
		}
		public SQLStatDataBuilder setRuntimeMemory(int runtimeMemory) {
			this.runtimeMemory = runtimeMemory;
			return this;
		}
		public SQLStatDataBuilder setPersistentMemory(int persistentMemory) {
			this.persistentMemory = persistentMemory;
			return this;
		}
		public SQLStatDataBuilder setDiskReads(int diskReads) {
			this.diskReads = diskReads;
			return this;
		}
		public SQLStatDataBuilder setBufferGets(int bufferGets) {
			this.bufferGets = bufferGets;
			return this;
		}
		public SQLStatDataBuilder setUserIOWaitTime(int userIOWaitTime) {
			this.userIOWaitTime = userIOWaitTime;
			return this;
		}
		public SQLStatDataBuilder setRowsProcessed(int rowsProcessed) {
			this.rowsProcessed = rowsProcessed;
			return this;
		}
		public SQLStatDataBuilder setOptimizerCost(int optimizerCost) {
			this.optimizerCost = optimizerCost;
			return this;
		}
		public SQLStatDataBuilder setElapsedTime(int elapsedTime) {
			this.elapsedTime = elapsedTime;
			return this;
		}
		public SQLStatDataBuilder setCommandType(int commandType) {
			switch (commandType)
			{
			case 2:
				this.commandType = CommandType.INSERT;
				break;
			case 3:
				this.commandType = CommandType.SELECT;
				break;
			case 6:
				this.commandType = CommandType.UPDATE;
				break;
			case 7:
				this.commandType = CommandType.DELETE;
				break;
			case 189:
				this.commandType = CommandType.MERGE;
				break;
			}
			return this;
		}
		public SQLStatDataBuilder setFirstLoadTime(Date firstLoadTime) {
			this.firstLoadTime = firstLoadTime;
			return this;
		}		
		public SQLStatDataBuilder setReplaceList(Map<String, List<String>> replaceList) {
			this.replaceList = replaceList;
			return this;
		}	
	}

	public String getSqlText() {
		return sqlText;
	}

	public int getCount() {
		return count;
	}

	public CommandType getCommandType() {
		return commandType;
	}

	public int getRuntimeMemory() {
		return runtimeMemory;
	}

	public int getPersistentMemory() {
		return persistentMemory;
	}

	public int getDiskReads() {
		return diskReads;
	}

	public int getBufferGets() {
		return bufferGets;
	}

	public int getUserIOWaitTime() {
		return userIOWaitTime;
	}

	public int getRowsProcessed() {
		return rowsProcessed;
	}

	public int getOptimizerCost() {
		return optimizerCost;
	}

	public int getElapsedTime() {
		return elapsedTime;
	}

	public void setSqlText(String sqlText) {
		this.sqlText = sqlText;
	}
	
	public void addTableName(String tableName)
	{
		this.tableNames.add(tableName);
	}
	
	public void addTableNames(Set<String> tableNames)
	{
		this.tableNames.addAll(tableNames);
	}
	
	public Date getFirstLoadTime() {
		return firstLoadTime;
	}

	public Set<String> getTableNames() {
		return tableNames;
	}

	public void addToReplaceList(String param, String replace)
	{
		List<String> values = this.replaceList.get(param);
		if(values == null)
		{
			values = new ArrayList<String>();
			this.replaceList.put(param, values);
		}
		values.add(replace);
	}
	
	public void addAllToReplaceList(String param, List<String> replaceList)
	{
		List<String> values = this.replaceList.get(param);
		if(values == null)
		{
			values = new ArrayList<String>();
			this.replaceList.put(param, values);
		}
		values.addAll(replaceList);
	}
	
	public Map<String, List<String>> getReplaceMap() {
		return replaceList;
	}
	
	public List<String> getReplaceList(String param) {
		return replaceList.get(param);
	}

	public double getUpdateFrequency() {
		return updateFrequency;
	}

	public void setUpdateFrequency(double updateFrequency) {
		this.updateFrequency = updateFrequency;
	}

	@Override
	public String toString() {
		return "SQLStatData [sqlText=" + sqlText + ", commandType="
				+ commandType + ", runtimeMemory=" + runtimeMemory
				+ ", persistentMemory=" + persistentMemory + ", diskReads="
				+ diskReads + ", bufferGets=" + bufferGets
				+ ", userIOWaitTime=" + userIOWaitTime + ", rowsProcessed="
				+ rowsProcessed + ", optimizerCost=" + optimizerCost
				+ ", elapsedTime=" + elapsedTime + "]";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((sqlText == null) ? 0 : sqlText.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		SQLStatData other = (SQLStatData) obj;
		if (sqlText == null) {
			if (other.sqlText != null)
				return false;
		} else if (sqlText.equals(other.sqlText))
			{
				/**
				 * Accumulate query statistics
				 */
				other.count++;
				other.bufferGets+=this.bufferGets;
				other.diskReads+=this.diskReads;
				other.elapsedTime+=this.elapsedTime;
				other.optimizerCost+=this.optimizerCost;
				other.persistentMemory+=this.persistentMemory;
				other.rowsProcessed+=this.rowsProcessed;
				other.runtimeMemory+=this.runtimeMemory;
				other.userIOWaitTime+=this.userIOWaitTime;
			return true;
			}
		return false;
	}	
	
	public void addUpdateNum (int num)
	{
		this.updateNum += num;
	}

	public int getUpdateNum() {
		return updateNum;
	}
}
